#ifndef CNC_BASE_REGISTER_IMPLEMENTATION_H
#define CNC_BASE_REGISTER_IMPLEMENTATION_H

#include "register.h"
#include "subject.h"
#include "datatypeid.h"
#include "changemgr.h"
#include <cassert>
#include <cstdint>

namespace cnc {

	namespace base {

		template <typename T> struct DataType2ID { enum { ID = DT_UNSUPPORTED }; };
		template <> struct DataType2ID<int8_t>   { enum { ID = DT_INT8        }; };
		template <> struct DataType2ID<uint8_t>  { enum { ID = DT_UINT8       }; };
		template <> struct DataType2ID<int16_t>  { enum { ID = DT_INT16       }; };
		template <> struct DataType2ID<uint16_t> { enum { ID = DT_UINT16      }; };
		template <> struct DataType2ID<int32_t>  { enum { ID = DT_INT32       }; };
		template <> struct DataType2ID<uint32_t> { enum { ID = DT_UINT32      }; };
		template <> struct DataType2ID<int64_t>  { enum { ID = DT_INT64       }; };
		template <> struct DataType2ID<uint64_t> { enum { ID = DT_UINT64      }; };
		template <> struct DataType2ID<float>    { enum { ID = DT_FLOAT32     }; };
		template <> struct DataType2ID<double>   { enum { ID = DT_FLOAT64     }; };

		template <DataTypeID T> struct ID2DataType { typedef void     type; };
		template <> struct ID2DataType<DT_INT8>    { typedef int8_t   type; };
		template <> struct ID2DataType<DT_UINT8>   { typedef uint8_t  type; };
		template <> struct ID2DataType<DT_INT16>   { typedef int16_t  type; };
		template <> struct ID2DataType<DT_UINT16>  { typedef uint16_t type; };
		template <> struct ID2DataType<DT_INT32>   { typedef int32_t  type; };
		template <> struct ID2DataType<DT_UINT32>  { typedef uint32_t type; };
		template <> struct ID2DataType<DT_INT64>   { typedef int64_t  type; };
		template <> struct ID2DataType<DT_UINT64>  { typedef uint64_t type; };
		template <> struct ID2DataType<DT_FLOAT32> { typedef float    type; };
		template <> struct ID2DataType<DT_FLOAT64> { typedef double   type; };

		class RegisterImpl : public Register, public Subject<RegisterImpl>
		{
			DataTypeID typeID_;
			void *data_;
			void *oldData_;
			template <typename T>
			void newData(T value)
			{
				T *data = new T;
				T *oldData = new T;
				*oldData = *data = value;
				data_ = data;
				oldData_ = oldData;
			}
		public:
			template <typename T>
			RegisterImpl(T value) : typeID_(DataType2ID<T>::ID)
			{
				assert(DataType2ID<T>::ID != DT_UNSUPPORTED);
				newData(value);
			}
			RegisterImpl(DataTypeID typeID) : typeID_(typeID)
			{
// 				Remark: next code does not work: unlike a type,
//				typeID is available only at runtime
//				newData(static_cast<ID2DataType<typeID>::type>(0)); 
				switch (typeID_) {
				case DT_INT8:
					newData(static_cast<int8_t>(0));
					break;
				case DT_UINT8:
					newData(static_cast<uint8_t>(0));
					break;
				case DT_INT16:
					newData(static_cast<int16_t>(0));
					break;
				case DT_UINT16:
					newData(static_cast<uint16_t>(0));
					break;
				case DT_INT32:
					newData(static_cast<int32_t>(0));
					break;
				case DT_UINT32:
					newData(static_cast<uint32_t>(0));
					break;
				case DT_INT64:
					newData(static_cast<int64_t>(0));
					break;
				case DT_UINT64:
					newData(static_cast<uint64_t>(0));
					break;
				case DT_FLOAT32:
					newData(static_cast<float>(0));
					break;
				case DT_FLOAT64:
					newData(static_cast<double>(0));
					break;
				case DT_UNSUPPORTED:
					assert(!"Invalid data type.");
				}
			}
			~RegisterImpl()
			{
// 				Remark: next code does not work: unlike a type,
//				typeID_ is available only at runtime
//				delete reinterpret_cast<ID2DataType<typeID_>::type *>(data_);
				switch (typeID_) {
				case DT_INT8:
					delete reinterpret_cast<int8_t *>(data_);
					break;
				case DT_UINT8:
					delete reinterpret_cast<uint8_t *>(data_);
					break;
				case DT_INT16:
					delete reinterpret_cast<int16_t *>(data_);
					break;
				case DT_UINT16:
					delete reinterpret_cast<uint16_t *>(data_);
					break;
				case DT_INT32:
					delete reinterpret_cast<int32_t *>(data_);
					break;
				case DT_UINT32:
					delete reinterpret_cast<uint32_t *>(data_);
					break;
				case DT_INT64:
					delete reinterpret_cast<int64_t *>(data_);
					break;
				case DT_UINT64:
					delete reinterpret_cast<uint64_t *>(data_);
					break;
				case DT_FLOAT32:
					delete reinterpret_cast<float *>(data_);
					break;
				case DT_FLOAT64:
					delete reinterpret_cast<double *>(data_);
					break;
				case DT_UNSUPPORTED:
					assert(!"Invalid data type.");
				}
			}
			unsigned short getBitSize()
			{
				static unsigned short size[] = {
					8, 8, 16, 16, 32, 32, 64, 64, 32, 64
				};
				return size[typeID_];
			}
			DataTypeID getDataType()
			{
				return typeID_;
			}
			template <typename T>
			bool set_(T value)
			{
				if (static_cast<DataTypeID>(DataType2ID<T>::ID) == typeID_) {
					*reinterpret_cast<T *>(oldData_) = *reinterpret_cast<T *>(data_);
					*reinterpret_cast<T *>(data_) = value;
					notify();
					return true;
				} else
					return false;
			}
			virtual bool set(int8_t   value) { return set_(value); }
			virtual bool set(uint8_t  value) { return set_(value); }
			virtual bool set(int16_t  value) { return set_(value); }
			virtual bool set(uint16_t value) { return set_(value); }
			virtual bool set(int32_t  value) { return set_(value); }
			virtual bool set(uint32_t value) { return set_(value); }
			virtual bool set(int64_t  value) { return set_(value); }
			virtual bool set(uint64_t value) { return set_(value); }
			virtual bool set(float    value) { return set_(value); }
			virtual bool set(double   value) { return set_(value); }
			template <typename T>
			bool get_(T *value)
			{
				if (static_cast<DataTypeID>(DataType2ID<T>::ID) == typeID_) {
					*value = *reinterpret_cast<T *>(data_);
					return true;
				} else
					return false;
			}
			virtual bool get(int8_t   *value) { return get_(value); }
			virtual bool get(uint8_t  *value) { return get_(value); }
			virtual bool get(int16_t  *value) { return get_(value); }
			virtual bool get(uint16_t *value) { return get_(value); }
			virtual bool get(int32_t  *value) { return get_(value); }
			virtual bool get(uint32_t *value) { return get_(value); }
			virtual bool get(int64_t  *value) { return get_(value); }
			virtual bool get(uint64_t *value) { return get_(value); }
			virtual bool get(float    *value) { return get_(value); }
			virtual bool get(double   *value) { return get_(value); }
			bool getBit(unsigned short index)
			{
				if (index >= getBitSize())
					return false;
				uint8_t *data = reinterpret_cast<uint8_t *>(data_);
				return (data[index / 8] & (1 << (index % 8))) != 0;
			}
		private:
			bool getOldBit(unsigned short index)
			{
				if (index >= getBitSize())
					return false;
				uint8_t *oldData = reinterpret_cast<uint8_t *>(oldData_);
				return (oldData[index / 8] & (1 << (index % 8))) != 0;
			}
		public:
			bool raisingEdge(unsigned short index)
			{
				return getOldBit(index) == false && getBit(index) == true;
			}
			bool fallingEdge(unsigned short index)
			{
				return getOldBit(index) == true && getBit(index) == false;
			}
		};

	} // namespace base

} // namespace cnc

#endif

